package com.example.demo.thread.ch4.case01;

import com.example.demo.thread.util.Debug;
import com.example.demo.thread.util.Tools;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 下载子任务
 *
 * @author: htc 《Java多线程编程实战指南（核心篇）》
 * @date: Created in 13:59 2019/1/8.
 */
public class DownloadTask implements Runnable {
    private final long lowerBound;
    private final long upperBound;
    private final DownloadBuffer xbuf;
    private final URL requestURL;
    private final AtomicBoolean cancelFlag;

    public DownloadTask(long lowerBound, long upperBound, URL requestURL, Storage storage, AtomicBoolean cancelFlag) {
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.requestURL = requestURL;
        this.xbuf = new DownloadBuffer(lowerBound, upperBound, storage);
        this.cancelFlag = cancelFlag;
    }

    /**
     * 对指定的URL发起http分段下载请求
     *
     * @param requestURL
     * @param lowerBound
     * @param upperBound
     * @return
     */
    private static InputStream issueRequest(URL requestURL, long lowerBound, long upperBound) throws IOException {
        Thread me = Thread.currentThread();
        Debug.info(me + "->[" + lowerBound + "," + upperBound + "]");
        final HttpURLConnection conn;
        InputStream in = null;
        conn = (HttpURLConnection) requestURL.openConnection();
        //配置连接超时时间
        String strConnTimeOut = System.getProperty("conn.timeout");
        int connTimeOut = null == strConnTimeOut ? 60000 : Integer.valueOf(strConnTimeOut);
        conn.setConnectTimeout(connTimeOut);

        //配置读取数据超时时间
        String strReadTimeOut = System.getProperty("read.timeout");
        int readTimeOut = null == strReadTimeOut ? 60000 : Integer.valueOf(strReadTimeOut);
        conn.setReadTimeout(readTimeOut);

        conn.setRequestMethod("GET");
        conn.setRequestProperty("Connection", "Keep-alive");
        //Range:byte=0-1024
        conn.setRequestProperty("Range", "bytes=" + lowerBound + "-" + upperBound);
        conn.setDoInput(true);
        conn.connect();

        int statusCode = conn.getResponseCode();
        if (HttpURLConnection.HTTP_OK != statusCode && HttpURLConnection.HTTP_PARTIAL != statusCode) {
            conn.disconnect();
            throw new IOException("Server exception, status code:" + statusCode);
        }

        //连接成功，开始下载数据
        Debug.info(me + "-Content-Range:" + conn.getHeaderField("Content-Range") + ",connection:" + conn.getHeaderField("connection"));
        in = new BufferedInputStream(conn.getInputStream()) {
            @Override
            public void close() throws IOException {
                try {
                    super.close();
                } finally {
                    conn.disconnect();
                }
            }
        };
        return in;
    }

    @Override
    public void run() {
        if (cancelFlag.get()) {
            return;
        }
        ReadableByteChannel channel = null;

        try {
            channel = Channels.newChannel(issueRequest(requestURL, lowerBound, upperBound));
            ByteBuffer buf = ByteBuffer.allocate(1024);
            while (!cancelFlag.get() && channel.read(buf) > 0) {
                //将从网络读取的数据写入缓冲区
                xbuf.write(buf);
                buf.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            Tools.silentClose(channel, xbuf);
        }
    }
}
